// 18.2 命名空间
/**
 * 大型程序往往会使用多个独立开发的库，这些库又会定义大量的全局名字，如类、函数和模板等。当应用程序用到多个供应商提供的库时，不可避免地会发生某些名字相互冲突的情况。
 *   多个库将名字放置在全局命名空间中将引发命名空间污染（namespace pollution）。
 * 命名空间（namespace）为防止名字冲突提供了更加可控的机制。命名空间分割了全局命名空间，其中每个命名空间是一个作用域。
 *   通过在某个命名空间中定义库的名字，库的作者（以及用户）可以避免全局名字固有的限制。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;
#include <functional>
#include "../VisualStudio2012/15/Quote.h"
#include "../VisualStudio2012/12/TextQuery.h"
#include <tuple>
#include <bitset>
#include <regex>
#include <random>
#include <cmath>
#include <iomanip>
#include <cstdio>

int main()
{
    // 18.2.1 命名空间定义
    /*一个命名空间的定义包含两部分：首先是关键字namespace，随后是命名空间的名字。在命名空间名字后面是一系列由花括号括起来的声明和定义。
        只要能出现在全局作用域中的声明就能置于命名空间内，主要包括：类、变量（及其初始化操作）、函数（及其定义）、模板和其他命名空间：
namespace cplusplus_primer {
  class Sales_data { }
  Sales_data operator+(const Sales_data&, const Sales_data&);
  class Query { }
  class Query_base {}
} // 命名空间结束后无须分号
      命名空间既可以定义在全局作用域内，也可以定义在其他命名空间中，但是不能定义在函数或类的内部。
    */

    // 每个命名空间都是一个作用域
    /*和其他作用域类似，命名空间中的每个名字都必须表示该空间内的唯一实体。因为不同命名空间的作用域不同，所以在不同命名空间内可以有相同名字的成员。
      位于该命名空间之外的代码则必须明确指出所用的名字属于哪个命名空间：[插图]
cplusplus_primer::Query q = cplusplus_primer::Query("hello");
    */

    // 命名空间可以是不连续的
    /*如我们在16.5节（第626页）介绍过的，命名空间可以定义在几个不同的部分，这一点与其他作用域不太一样。编写如下的命名空间定义：[插图]
namespace nsp {
  // 相关声明
}
      可能是定义了一个名为nsp的新命名空间，也可能是为已经存在的命名空间添加一些新成员。
      命名空间的定义可以不连续的特性使得我们可以将几个独立的接口和实现文件组成一个命名空间。此时，命名空间的组织方式类似于我们管理自定义类及函数的方式：
        · 命名空间的一部分成员的作用是定义类，以及声明作为类接口的函数及对象，则这些成员应该置于头文件中，这些头文件将被包含在使用了这些成员的文件中。
        · 命名空间成员的定义部分则置于另外的源文件中。
      定义多个类型不相关的命名空间应该使用单独的文件分别表示每个类型（或关联类型构成的集合）。
    */

    // 定义本书的命名空间
    /*通过使用上述接口与实现分离的机制，我们可以将cplusplus_primer库定义在几个不同的文件中。例如：
// ---- Sales_data.h ----
// #include应该出现在打开命名空间的操作之前
#include <string>
namespace cplusplus_primer {
  class Sales_data { }
  Sales_data operator+(const Sales_data&, const Sales_data&);
  // Sales_data的其他接口函数声明
}
// ---- Sales_data.cc ----
// 确保#include出现在打开命名空间的操作之前
#include "Sales_data.h"
namespace cplusplus_primer {
// Sales_data成员及重载运算符的定义
}
      程序如果想使用我们定义的库，必须包含必要的头文件，这些头文件中的名字定义在命名空间cplusplus_primer内：[插图]
// ---- user.cc ----
// Sales_data.h头文件的名字位于命名空间cplusplus_primer内
#include "Sales_data.h"
int main()
{
  using cplusplus_primer::Sales_data;
  Sales_data s1, s2;
  // ...
  return 0;
}
      这种程序的组织方式提供了开发者和库用户所需的模块性。
      有一点需要注意，在通常情况下，我们不把#include放在命名空间内部。如果我们这么做了，隐含的意思是把头文件中所有的名字定义成该命名空间的成员。
        例如，如果Sales_data.h在包含string头文件前就已经打开了命名空间cplusplus_primer，则程序将出错，因为这么做意味着我们试图将命名空间std嵌套在命名空间cplusplus_primer中。
    */

    // 定义命名空间成员
    /*假定作用域中存在合适的声明语句，则命名空间中的代码可以使用同一命名空间定义的名字的简写形式：[插图]
#include "Sales_data.h"
namespace cplusplus_primer { // 重新打开命名空间cplusplus_primer
// 名空间中定义的成员可以直接使用名字，此时无须前缀
std::istream&
operator>(std::istream& in, Sqles_data& s) { }
}
      也可以在命名空间定义的外部定义该命名空间的成员。命名空间对于名字的声明必须在作用域内，同时该名字的定义需要明确指出其所属的命名空间：[插图]
// 命名空间之外定义的成员必须使用含有前缀的名字
cplusplus_primer::Sales_data
cplusplus_primer::operator+(const Sqles_data& lhs, const Sqles_data& rhs)
{
  Sales_data ret(lhs);
  // ...
}
      在命名空间cplusplus_primer内部，我们可以直接使用该命名空间的其他成员，比如在上面的代码中，可以直接使用Sales_data定义函数的形参。
    */

    // 模板特例化
    /*模板特例化必须定义在原始模板所属的命名空间中（参见16.5节，第626页）。和其他命名空间名字类似，只要我们在命名空间中声明了特例化，就能在命名空间外部定义它了：[插图]
// 们必须将模板特例化声明成std的成员
namespace std {
  template<> struct hash<Sales_data>;
}
// 在std中添加了模板特例化的声明后，就可以在命名空间std的外部定义它了
template<> struct std::hash<Sales_data>
{
  size_t operator()(const Sales_data& sd) const {
    return hash<string>()(s.bookNo) ^ hash<unsigned>()(s.units_sold) ^ hash<double>()(s.revenue);
  }
  // 其他成员与之前的版本一致
}
    */

    // 全局命名空间
    /*全局作用域中定义的名字（即在所有类、函数及命名空间之外定义的名字）也就是定义在全局命名空间（global namespace）中。全局命名空间以隐式的方式声明，并且在所有程序中都存在。
        全局作用域中定义的名字被隐式地添加到全局命名空间中。
      作用域运算符同样可以用于全局作用域的成员，因为全局作用域是隐式的，所以它并没有名字。下面的形式[插图] ::member_name
    */

    // 嵌套的命名空间
    /*嵌套的命名空间是指定义在其他命名空间中的命名空间：[插图]
namespace cplusplus_primer {
  // 一个嵌套的命名空间：定义了库的Query部分
  namespace QueryLib {
    class Query { }
    Query operator&(const Query&, const Query&);
    // ...
  }
  // 二个嵌套的命名空间：定义了库的Sales_data部分
  namespace Bookstore {
    class Quote { }
    class Disc_quote : public Quote { }
    // ...
  }
}
      嵌套的命名空间同时是一个嵌套的作用域，它嵌套在外层命名空间的作用域中。嵌套的命名空间中的名字遵循的规则与往常类似：内层命名空间声明的名字将隐藏外层命名空间声明的同名成员。
        在嵌套的命名空间中定义的名字只在内层命名空间中有效，外层命名空间中的代码要想访问它必须在名字前添加限定符。例如，在嵌套的命名空间QueryLib中声明的类名是：
cplusplus_primer::QueryLib::Query
    */

    // 内联命名空间
    /*C++11新标准引入了一种新的嵌套命名空间，称为内联命名空间（inline namespace）。和普通的嵌套命名空间不同，内联命名空间中的名字可以被外层命名空间直接使用。
        也就是说，我们无须在内联命名空间的名字前添加表示该命名空间的前缀，通过外层命名空间的名字就可以直接访问它。
      定义内联命名空间的方式是在关键字namespace前添加关键字inline：[插图]
inline namespace FifthEd {
  // 该命名空间表示本书第5版的代码
}
namespace FifthEd { // 隐式内联
  class Query_base { }
  // 其他与Query有关的声明
}
      关键字inline必须出现在命名空间第一次定义的地方，后续再打开命名空间的时候可以写inline，也可以不写。
      当应用程序的代码在一次发布和另一次发布之间发生了改变时，常常会用到内联命名空间。例如，我们可以把本书当前版本的所有代码都放在一个内联命名空间中，而之前版本的代码都放在一个非内联命名空间中：[插图]
namespace FourthEd {
  class Item_base { }
  class Query_base { }
  // 本书第4版用到的其他代码
}
      命名空间cplusplus_primer将同时使用这两个命名空间。例如，假定每个命名空间都定义在同名的头文件中，则我们可以把命名空间cplusplus_primer定义成如下形式：[插图]
namespace cplusplus_primer {
#include "FourthEd.h"
#include "FifthEd.h"
}
      因为FifthEd是内联的，所以形如cplusplus_primer::的代码可以直接获得FifthEd的成员。如果我们想使用早期版本的代码，则必须像其他嵌套的命名空间一样加上完整的外层命名空间名字，
        比如cplusplus_primer::FourthEd::Query_base。
    */

    // 未命名的命名空间
    /*未命名的命名空间（unnamed namespace）是指关键字namespace后紧跟花括号括起来的一系列声明语句。未命名的命名空间中定义的变量拥有静态生命周期：它们在第一次使用前创建，并且直到程序结束才销毁。
      一个未命名的命名空间可以在某个给定的文件内不连续，但是不能跨越多个文件。每个文件定义自己的未命名的命名空间，如果两个文件都含有未命名的命名空间，则这两个空间互相无关。
        在这两个未命名的命名空间中可以定义相同的名字，并且这些定义表示的是不同实体。如果一个头文件定义了未命名的命名空间，则该命名空间中定义的名字将在每个包含了该头文件的文件中对应不同实体。
      和其他命名空间不同，未命名的命名空间仅在特定的文件内部有效，其作用范围不会横跨多个不同的文件。
      未命名的命名空间中定义的名字的作用域与该命名空间所在的作用域相同。如果未命名的命名空间定义在文件的最外层作用域中，则该命名空间中的名字一定要与全局作用域中的名字有所区别：[插图][插图]
int i; // i的全局声明
namespace {
  int i;
}
i= 10; // 二义性：i的定义既出现在全局作用域中，又出现在未嵌套的未命名的命名空间中
      和所有命名空间类似，一个未命名的命名空间也能嵌套在其他命名空间当中。此时，未命名的命名空间中的成员可以通过外层命名空间的名字来访问：[插图]
namespace local {
  namespace {
    int i;
  }
}
local::i = 42; // 正确：定义在嵌套的为命名空间中的i与全局作用域中的i不通
    */

    // 未命名的命名空间取代文件中的静态声明
    // 在标准C++引入命名空间的概念之前，程序需要将名字声明成static的以使得其对于整个文件有效。在文件中进行静态声明的做法是从C语言继承而来的。在C语言中，声明为static的全局实体在其所在的文件外不可见。
    // 在文件中进行静态声明的做法已经被C++标准取消了，现在的做法是使用未命名的命名空间。

    // 18.2.2 使用命名空间成员
    /*像namespace_name：：member_name这样使用命名空间的成员显然非常烦琐，特别是当命名空间的名字很长时尤其如此。幸运的是，我们可以通过一些其他更简便的方法使用命名空间的成员。
        之前的程序已经使用过其中一种方法，即using声明（参见3.1节，第74页）。本节还将介绍另外几种方法，如命名空间的别名以及using指示等。
    */

    // 命名空间的别名
    /*命名空间的别名（namespace alias）使得我们可以为命名空间的名字设定一个短得多的同义词。例如，一个很长的命名空间的名字形如[插图]
namespace cplusplus_primer { };
      我们可以为其设定一个短得多的同义词：[插图]
namespace primer = cplusplus_primer;
      不能在命名空间还没有定义前就声明别名，否则将产生错误。
      命名空间的别名也可以指向一个嵌套的命名空间：[插图]
namespace Qlib = cplusplus_primer::Query_lib;
Qlib::Query q;
      一个命名空间可以有好几个同义词或别名，所有别名都与命名空间原来的名字等价。
    */

    // using声明：扼要概述
    /*一条using声明（using declaration）语句一次只引入命名空间的一个成员。它使得我们可以清楚地知道程序中所用的到底是哪个名字。
      using声明引入的名字遵守与过去一样的作用域规则：它的有效范围从using声明的地方开始，一直到using声明所在的作用域结束为止。在此过程中，外层作用域的同名实体将被隐藏。
        未加限定的名字只能在using声明所在的作用域以及其内层作用域中使用。在有效作用域结束后，我们就必须使用完整的经过限定的名字了。
    */

    // using指示
    /*using指示（using directive）和using声明类似的地方是，我们可以使用命名空间名字的简写形式；和using声明不同的地方是，我们无法控制哪些名字是可见的，因为所有名字都是可见的。
      using指示以关键字using开始，后面是关键字namespace以及命名空间的名字。如果这里所用的名字不是一个已经定义好的命名空间的名字，则程序将发生错误。
        using指示可以出现在全局作用域、局部作用域和命名空间作用域中，但是不能出现在类的作用域中。
      如果我们提供了一个对std等命名空间的using指示而未做任何特殊控制的话，将重新引入由于使用了多个库而造成的名字冲突问题。
    */

    // using指示与作用域
    /*using指示引入的名字的作用域远比using声明引入的名字的作用域复杂。
      如我们所知，using声明的名字的作用域与using声明语句本身的作用域一致，从效果上看就好像using声明语句为命名空间的成员在当前作用域内创建了一个别名一样。
      using指示所做的绝非声明别名这么简单。相反，它具有将命名空间成员提升到包含命名空间本身和using指示的最近作用域的能力。
      对于using声明来说，我们只是简单地令名字在局部作用域内有效。相反，using指示是令整个命名空间的所有内容变得有效。
        通常情况下，命名空间中会含有一些不能出现在局部作用域中的定义，因此，using指示一般被看作是出现在最近的外层作用域中。
      在最简单的情况下，假定我们有一个命名空间A和一个函数f，它们都定义在全局作用域中。如果f含有一个对A的using指示，则在f看来，A中的名字仿佛是出现在全局作用域中f之前的位置一样：[插图]
// 命名空间A和函数f定义在全局作用域中
namespace A {
  int i, j;
}
void f()
{
  using namespace A;     // A中的名字注入到全局作用域中
  cout << i * j << endl; // 使用命名空间A中的i和j
  // ...
}
    */
    
    // using指示示例
    /*
namespace blip {
  int i=16, j=15, k=23;
  // 其他声明
}
int j = 0; // 正确：blip的j隐藏在明明空间中
void manip()
{
  // using指示，blip中的名字被“添加”到全局作用域中
  using namespace blip; // 如果使用了j，则将在::j和blip::j之间产生冲突
  ++i;        // 将blip::i设定为17
  ++j;        // 二义性错误：是全局的j还是blip::j？
  ++::j;      // 正确：将全局的j设定为1
  ++blip::j;  // 正确：将blip：：j设定为16
  int k = 97; // 当前局部的k隐藏了blip：:k
  ++k;        // 将当前局部的k设定为98
}
    */

    // 头文件与using声明或指示
    /*头文件如果在其顶层作用域中含有using指示或using声明，则会将名字注入到所有包含了该头文件的文件中。通常情况下，头文件应该只负责定义接口部分的名字，而不定义实现部分的名字。
        因此，头文件最多只能在它的函数或命名空间内使用using指示或using声明（参见3.1节，第75页）。
    */

    // 提示：避免using指示
    /*using指示一次性注入某个命名空间的所有名字，这种用法看似简单实则充满了风险：只使用一条语句就突然将命名空间中所有成员的名字变得可见了。
        如果应用程序使用了多个不同的库，而这些库中的名字通过using指示变得可见，则全局命名空间污染的问题将重新出现。
      另一个风险是由using指示引发的二义性错误只有在使用了冲突名字的地方才能被发现。这种延后的检测意味着可能在特定库引入很久之后才爆发冲突。
      相比于使用using指示，在程序中对命名空间的每个成员分别使用using声明效果更好，这么做可以减少注入到命名空间中的名字数量。
        using声明引起的二义性问题在声明处就能发现，无须等到使用名字的地方，这显然对检测并修改错误大有益处。
      using指示也并非一无是处，例如在命名空间本身的实现文件中就可以使用using指示。
    */

    // 18.2.3 类、命名空间与作用域
    /*对命名空间内部名字的查找遵循常规的查找规则：即由内向外依次查找每个外层作用域。外层作用域也可能是一个或多个嵌套的命名空间，直到最外层的全局命名空间查找过程终止。
namespace A {
  int i;
  namespace B {
    int i;      // 在B中隐藏了A::i
    int j;
    int f1()
    {
      int j;    // j是f1的局部变量，隐藏了A::B::j
      return i; // 返回B::i
    }
  } // 名空间B结束，此后B中定义的名字不再可见
  int f2() {
    return j;   // 错误：j没有被定义
  }
  int j = i;    // A::i进行初始化
}
      对于位于命名空间中的类来说，常规的查找规则仍然适用：当成员函数使用某个名字时，首先在该成员中进行查找，然后在类中查找（包括基类），接着在外层作用域中查找
namespace A {
  int i;
  int k;
  class C1 {
  public:
    C1():i(0), j(0) { }    // 正确：初始化C1::i和C1::j
    int f1() { return k; } // 返回A::k
    int f2() { return h; } // 错误：h没有被定义 
    int f3()
  private:
    int i;                 // 在C1中隐藏了A::i
    int j;
  };
  int h = i;                  // 用A::i进行初始化
}
// 成员f3定义在C1和命名空间A的外部
int A::C1::f3() { return h; } // 正确：返回A::h
      除了类内部出现的成员函数定义之外（参见7.4.1节，第254页），总是向上查找作用域。名字必须先声明后使用，因此f2的return语句无法通过编译。该语句试图使用命名空间A的名字h，但此时h尚未定义。
        如果h在A中定义的位置位于C1的定义之前，则上述语句将合法。类似的，因为f3的定义位于A：：h之后，所以f3对于h的使用是合法的。
    */

    // 实参相关的查找与类类型形参
    /*考虑下面这个简单的程序：[插图]
std::string s;
std::cin >> s;
      如我们所知，该调用等价于（参见14.1节，第491页）：[插图]
operator>>(std::cin, s);
      operator>>函数定义在标准库string中，string又定义在命名空间std中。但是我们不用std：：限定符和using声明就可以调用operator>>。
      对于命名空间中名字的隐藏规则来说有一个重要的例外，它使得我们可以直接访问输出运算符。这个例外是，当我们给函数传递一个类类型的对象时，除了在常规的作用域查找外还会查找实参类所属的命名空间。
        这一例外对于传递类的引用或指针的调用同样有效。
      在此例中，当编译器发现对operator>>的调用时，首先在当前作用域中寻找合适的函数，接着查找输出语句的外层作用域。随后，因为>>表达式的形参是类类型的，所以编译器还会查找cin和s的类所属的命名空间。
        也就是说，对于这个调用来说，编译器会查找定义了istream和string的命名空间std。当在std中查找时，编译器找到了string的输出运算符函数。
    */
    
    // 查找与std::move和std::forward
    /*很多甚至是绝大多数C++程序员从来都没有考虑过与实参相关的查找问题。通常情况下，如果在应用程序中定义了一个标准库中已有的名字，则将出现以下两种情况中的一种：
        要么根据一般的重载规则确定某次调用应该执行函数的哪个版本；要么应用程序根本就不会执行函数的标准库版本。
      接下来考虑标准库move和forward函数。这两个都是模板函数，在标准库的定义中它们都接受一个右值引用的函数形参。如我们所知，在函数模板中，右值引用形参可以匹配任何类型（参见16.2.6节，第611页）。
        如果我们的应用程序也定义了一个接受单一形参的move函数，则不管该形参是什么类型，应用程序的move函数都将与标准库的版本冲突。forward函数也是如此。
      对于move和forward来说，冲突很多但是大多数是无意的，这一特点解释了为什么我们建议最好使用它们的带限定语的完整版本的原因（参见12.1.5节，第417页）。
        通过书写std：：move而非move，我们就能明确地知道想要使用的是函数的标准库版本。
    */

    // 友元声明与实参相关的查找
    /*回顾我们曾经讨论过的，当类声明了一个友元时，该友元声明并没有使得友元本身可见（参见7.2.1节，第242页）。
        然而，一个另外的未声明的类或函数如果第一次出现在友元声明中，则我们认为它是最近的外层命名空间的成员。这条规则与实参相关的查找规则结合在一起将产生意想不到的效果：[插图]
namespace A {
  class C {
    // 两个友元，在友元声明之外没有其他的声明
    // 这些函数隐式地成为命名空间A的成员
    friend void f2();        // 除非另有声明，否则不会被找到
    friend void f(const C&); // 参相关的查找规则可以被找到
  };
}
        此时，f和f2都是命名空间A的成员。即使f不存在其他声明，我们也能通过实参相关的查找规则调用f：[插图]
int main()
{
  A::C cobj;
  f(cobj); // 正确：通过在A：：C中的友元声明找到A：：f
  f2();    // 错误：A：：f2没有被声明
}
        因为f接受一个类类型的实参，而且f在C所属的命名空间进行了隐式的声明，所以f能被找到。相反，因为f2没有形参，所以它无法被找到。
    */

    // 18.2.4 重载与命名空间
    // 命名空间对函数的匹配过程有两方面的影响（参见6.4节，第209页）。其中一个影响非常明显：using声明或using指示能将某些函数添加到候选函数集。另外一个影响则比较微妙。

    // 与实参相关的查找与重载
    /*在上一节中我们了解到，对于接受类类型实参的函数来说，其名字查找将在实参类所属的命名空间中进行。这条规则对于我们如何确定候选函数集同样也有影响。
        我们将在每个实参类（以及实参类的基类）所属的命名空间中搜寻候选函数。在这些命名空间中所有与被调用函数同名的函数都将被添加到候选集当中，即使其中某些函数在调用语句处不可见也是如此：[插图]
namespace NS {
  class Quote { };
  void display(const Quote&) { }
}
// Bulk_item的基类声明在命名空间NS中
class Bulk_item : pubic NS::Quote { };
int main() {
  Bulk_item book1;
  display(book1);
  return 0;
}
      我们传递给display的实参属于类类型Bulk_item，因此该调用语句的候选函数不仅应该在调用语句所在的作用域中查找，而且也应该在Bulk_item及其基类Quote所属的命名空间中查找。
        命名空间NS中声明的函数display（const Quote&）也将被添加到候选函数集当中。
    */

    // 重载与using声明
    /*要想理解using声明与重载之间的交互关系，必须首先明确一条：using声明语句声明的是一个名字，而非一个特定的函数（参见15.6节，第551页）：[插图]
using NS::print(int); // 错误：不能指定形参列表
using NS::print;      // 正确：using声明只声明一个名字
      当我们为函数书写using声明时，该函数的所有版本都被引入到当前作用域中。
      一个using声明引入的函数将重载该声明语句所属作用域中已有的其他同名函数。如果using声明出现在局部作用域中，则引入的名字将隐藏外层作用域的相关声明。
        如果using声明所在的作用域中已经有一个函数与新引入的函数同名且形参列表相同，则该using声明将引发错误。
    */

    // 重载与using指示
    /*using指示将命名空间的成员提升到外层作用域中，如果命名空间的某个函数与该命名空间所属作用域的函数同名，则命名空间的函数将被添加到重载集合中：[插图]
namespace libs_R_us {
  extern void print(int);
  extern void print(double);
}
// 普通的声明
void print(const std::string &);
// 这个using指示把名字添加到print调用的候选函数集
using namespace libs_R_us;
// print调用此时的候选函数包括：
// libs R_us的print(int)
// libs_R_us的print(double)
// 显式声明的print（const std::string &)
void fooBar(int iVal)
{
  print("Value: "); // 调用全局函数print(conststring &)
  print(iVal);      // 调用libs_R_us::print(int)
}
      与using声明不同的是，对于using指示来说，引入一个与已有函数形参列表完全相同的函数并不会产生错误。此时，只要我们指明调用的是命名空间中的函数版本还是当前作用域的版本即可。
    */

    // 跨越多个using指示的重载
    /*如果存在多个using指示，则来自每个命名空间的名字都会成为候选函数集的一部分：[插图][插图]
namespace AW {
  int print(int);
}
namespace Primer {
  double print(double);
}
// using指示从不同的命名空间中创建了一个重载函数集合
using namespace AW;
using namespace Primer;
long double print(long double);
int main() {
  print(1);   // 调用AW::print
  print(3.1); // 调用Primer::print
  return 0;
}
    */

    return 0;
}